/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package mb.monitor;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.net.Socket;
import java.text.DecimalFormat;
import mb.config.MonitorConfig;
import mb.mbmiddle.MbPartMiddle;
import mb.utils.LogText;
/**
 *
 * @author thanh.nguyenchi
 */
public class MonitorHandler extends Thread {
    private Socket socket;
    private int clientNumber;
    private boolean isLogin = false;
    private MbPartMiddle objMain;
    private LogText objLog;
    private MonitorConfig objConfig;
    private String ip;
    
    
    String green = "\u001B[1;32m";
    String red = "\u001B[1;31m";
    String black = "\u001B[0;30m";
    String yellow = "\u001B[0;33m";
    String white = "\u001B[0;37m";

    public MonitorHandler(Socket socket, int clientNumber,MbPartMiddle objMain,LogText objLog,MonitorConfig objConfig) {
        this.objMain = objMain;
        this.socket = socket;
        this.clientNumber = clientNumber; 
        this.objLog = objLog;
        this.objConfig = objConfig;
        String remoteAddress = socket.getRemoteSocketAddress().toString();
        String[] info = remoteAddress.split("\\:");
        if (info.length == 2){
            this.ip = info[0];
        }
        else{
            this.ip = "255.255.255.255";
        }
        
        if(objConfig.isCheckIP(ip)){
            log("Accept new connection with client# " + clientNumber + " ip : " + this.ip);
        }else{
            log("Invalid ip : " + this.ip);
            try{
                socket.close();
            }
            catch (Exception ex){                
            }            
        }
        
        
    }

    public void run() {
        try {

            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

            // Send a welcome message to the client.
            out.println("Welcome to webserver monitor");
            out.print("Login (userName):");
            out.flush();

            while (true) {
                String input = in.readLine();
                if (input == null || input.equals("bye")) {
                    break;
                }
                // Process message
                processMessage(out,input);                    
            }
        } catch (IOException e) {
            log("Error handling client# " + clientNumber + ": " + e);
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                log("[client#" + this.clientNumber + "] err=" + e.toString()); 
            }
            log("Connection with client# " + clientNumber + " closed");
        }
    }

    private void log(String message) {
        objLog.log(message);
    }

    private String viewThread(){
        String newLine = System.getProperty("line.separator");
        int milisecond = 1000;
        String sResult = "";
        DecimalFormat df = new DecimalFormat("###.##");
        // TODO code application logic here
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        
        threadMXBean.setThreadContentionMonitoringEnabled(true);
	threadMXBean.setThreadCpuTimeEnabled(true);

        ThreadInfo[] threadInfo = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds());
        int i = 1;        
        //header
        sResult = "  No Name\t   Cpu time(ms)\t      User time(ms)\tBlocked(ms)\t" + newLine;
        for (ThreadInfo threadInfo2 : threadInfo) {
            long blockedTime = threadInfo2.getBlockedTime()/milisecond;
            long waitedTime = threadInfo2.getWaitedTime()/milisecond;
            long cpuTime = threadMXBean.getThreadCpuTime(threadInfo2.getThreadId())/milisecond;
            long userTime = threadMXBean.getThreadUserTime(threadInfo2.getThreadId())/milisecond;
            
            if ((cpuTime > 0) || (blockedTime > 0)){
                String threadName = threadInfo2.getThreadName().trim();
                if (threadName.length() > 10){
                    threadName = threadName.substring(0, 10);
                }
                String msg = i + " " + threadName;
                msg = msg + "\t\t" + df.format(cpuTime);
                msg = msg + "\t\t" + df.format(userTime);
                msg = msg + "\t\t" + df.format(blockedTime);
                //msg = msg + "\t\t" + df.format(waitedTime);
                msg = msg + newLine;
                sResult = sResult + msg;
                i++;
            }
        }
        return sResult;
    }
    
    private void processMessage(PrintWriter out,String input){
        String sCmd = input;
        // Chua login --> Kiem tra user
        if (isLogin == false){
            if (objConfig.isCheckUserIP(input,this.ip)){
                isLogin = true;
                String sReturn = "login is sucessful.Enter command:";
                out.println(sReturn);                    
            }
            else{           
                String sReturn = "invalid userName. Please login:";
                out.print(sReturn);  
                out.flush();
            }
        }
        else{
            // parse for cmd
            String newLine = System.getProperty("line.separator");
            if (sCmd.equals("viewos")){
                StringBuffer buf = new StringBuffer();
                buf.append("MB Middle Information" + newLine);
                buf.append(" 1) OS\t\t: " + System.getProperty("os.name"));  
                buf.append(" version " + System.getProperty("os.version") + newLine);
                buf.append(" 2) Number CPU\t: " + Runtime.getRuntime().availableProcessors() + newLine);                  
                buf.append(" 3) JVM\t\t: " + System.getProperty("java.vm.name"));
                buf.append(" version " + System.getProperty("java.runtime.version") + newLine);
                DecimalFormat df = new DecimalFormat("###.##");
                // -Xmx value
                int convert = 1048575;
                long maxMemory = Runtime.getRuntime().maxMemory();  
                // total memory
                long totalMemory = Runtime.getRuntime().totalMemory();
                long freeMemory = Runtime.getRuntime().freeMemory();
                long usedMemory = totalMemory - freeMemory;
                buf.append(" 4) Max Memory\t: " + green + df.format(maxMemory/convert) + white + " MB" + newLine);
                buf.append(" 5) TotalMemory\t: " + green + df.format(totalMemory/convert) + white + " MB" + newLine);
                buf.append(" 6) Free Memory\t: " + green + df.format(freeMemory/convert) + white + " MB" + newLine);
                buf.append(" 7) Used Memory\t: " + green + df.format(usedMemory/convert) + white + " MB" + newLine);
                out.print(buf.toString());
                out.flush();
            }
            else if(sCmd.equals("restart")){
                String sReturn = "====> Restart webserver ....";
                out.println(sReturn);
                objMain.stopWebserver();
                sReturn = "    webserver is stopped.";
                out.println(sReturn);
                objMain.startWebsever();
                sReturn = "    webserver is started.";
                out.println(sReturn);  
                out.flush();
            }
            else if (sCmd.endsWith("memory")){
                StringBuffer buf = new StringBuffer();
                buf.append("Memory ...." + newLine);
                long memory = 0; //MemoryMeasurer.measureBytes(objMain.objMemory);
                buf.append("    Global Memory\t: " + green + memory + white + " bytes" + newLine);
                out.print(buf.toString());
                out.flush();
            }
            else if (sCmd.endsWith("reload")){
                StringBuffer buf = new StringBuffer();
                buf.append("Reload ...." + newLine);
                out.print(buf.toString());
                out.flush();
                
                objMain.statInMemory();
            }
            else if (sCmd.endsWith("thread")){
                StringBuffer buf = new StringBuffer();
                buf.append(viewThread());
                out.print(buf.toString());
                out.flush();
            }
            else if (sCmd.endsWith("help")){
                StringBuffer buf = new StringBuffer();
                buf.append("Monitor command ...." + newLine);
                buf.append("         viewos\t: " + "view os information." + newLine);
                buf.append("         memory\t: " + "view memory of mbpart." +newLine);
                buf.append("        restart\t: " + "restart webserver." +newLine);
                buf.append("         reload\t: " + "reload inmemory." +newLine);
                buf.append("          clear\t: " + "clear screen." +newLine);
                out.print(buf.toString());
                out.flush();
            }
            else if (sCmd.endsWith("clear")){
                out.println("\u001B[2J");
                out.flush();
            }
            else{
                out.println("invalid command.");
                out.flush();
            }   
        }

    }
}
